package com.wpre.lotterysystem.service.Impl;

import com.wpre.lotterysystem.common.errcode.ServiceErrorCodeConstants;
import com.wpre.lotterysystem.common.exception.ServiceException;
import com.wpre.lotterysystem.common.utils.JacksonUtil;
import com.wpre.lotterysystem.common.utils.RedisUtil;
import com.wpre.lotterysystem.controller.param.DrawPrizeParam;
import com.wpre.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.wpre.lotterysystem.dao.dataobject.*;
import com.wpre.lotterysystem.dao.mapper.*;
import com.wpre.lotterysystem.service.DrawPrizeService;
import com.wpre.lotterysystem.service.dto.WinningRecordDTO;
import com.wpre.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.wpre.lotterysystem.service.enums.ActivityStatusEnum;
import com.wpre.lotterysystem.service.enums.ActivityTiersEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.wpre.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.wpre.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: LianBao
 * Date: 2024-12-22
 * Time: 20:08
 */
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private static final Logger log = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    private final Long WINNING_RECORDS_TIME_OUT = 60 * 60 * 24 * 2L;

    private final String WINNING_RECORDS_PREFIX = "WinningRecords_";

    @Override

    public void drawPrize(DrawPrizeParam param) {
        //1.发消息
        //参数: 交换机 绑定的key 消息体
        Map<String, String> map = new HashMap<>();
        map.put("messageId", String.valueOf(UUID.randomUUID()));//消息id
        map.put("messageData", JacksonUtil.writeValueAsString(param));//消息体

        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);//转换成json后进行发送

    }

    /**
     * 校验抽奖请求参数
     *
     * @param param
     */
    @Override
    public boolean checkParam(DrawPrizeParam param) {
        //1.判断活动是否存在
        //活动
        ActivityDO activityDO = activityMapper.selectActivityById(param.getActivityId());
        //奖品
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAidPid(param.getActivityId(), param.getPrizeId());


        if (activityDO == null || activityPrizeDO == null) {
            //如果查询出来的活动和奖品在数据库中都不存在
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_NOT_EXIST_ERROR);
            return false;
        }

        //判断活动是否有效
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.DONE.name())) {
            //活动已完成
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_DONE);
            return false;
        }

        //判断奖品是否有效
        if (activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.DONE.name())) {
//            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_DONE);
            return false;
        }

        //中奖者人数是否和设置的奖品数量一致
        if (activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
//            throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT);
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询相关信息:活动,人员,奖品,活动关联奖品表
        //活动
        ActivityDO activityDO = activityMapper.selectActivityById(param.getActivityId());
        //用户
        List<UserDO> userDOList = userMapper.batchSelectByIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList())
        );
        //奖品
        PrizeDO prizeDO = prizeMapper.SelectById(param.getPrizeId());
        //活动关联的奖品表
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAidPid(param.getActivityId(), param.getPrizeId());
        //构造中奖记录,保存
        List<WinningRecordDO> winningRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());
                    return winningRecordDO;
                }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winningRecordDOList);


        //缓存中奖者记录
        //1.奖品维度的中奖记录(单个奖品下)(WinningRecords_activity_Id_prizeId ,winningRecordDOList(奖品维度的中奖名单))
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId(), winningRecordDOList, WINNING_RECORDS_TIME_OUT);
        //2.活动级别的中奖记录(完整的记录)(WinningRecords_activityId,winningRecordDOList(活动维度的中奖名单))
        //活动已完成后再进行缓存
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.DONE.name())) {
            //缓存
            //查询活动维度的全量中奖记录
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(param.getActivityId());

            cacheWinningRecords(String.valueOf(param.getActivityId()), allList, WINNING_RECORDS_TIME_OUT);
        }
        return winningRecordDOList;

    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if (activityId == null) {
            return;
        }
        //1.删除数据表
        winningRecordMapper.deleteRecord(activityId, prizeId);
        //2.删除缓存
        if (prizeId != null) {
            //删除奖品维度的缓存
            deleteWinningRecords(activityId + "_" + prizeId);
        }
        //如果没有传递prizeId,删除活动维度的
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        //1.查询Redis
        String key = param.getPrizeId() == null ?
                String.valueOf(param.getActivityId()) :
                param.getActivityId() + "_" + param.getPrizeId();
        List<WinningRecordDO> winningRecords = getWinningRecords(key);
        if (winningRecords != null) {
            return convertToWinningRecordDTOList(winningRecords);
        }
        //2.如果Redis不存在,查库
        winningRecords = winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(), param.getPrizeId());
        if (winningRecords == null) {
            return Arrays.asList();
        }

        //整合,存放到Redis
        cacheWinningRecords(key, winningRecords, WINNING_RECORDS_TIME_OUT);

        return convertToWinningRecordDTOList(winningRecords);
    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(List<WinningRecordDO> winningRecords) {
        if (winningRecords == null) {
            return Arrays.asList();
        }
        return winningRecords.stream().map(
                winningRecordDO -> {
                    WinningRecordDTO recordDTO = new WinningRecordDTO();
                    recordDTO.setWinnerId(winningRecordDO.getWinnerId());
                    recordDTO.setWinnerName(winningRecordDO.getWinnerName());
                    recordDTO.setPrizeName(winningRecordDO.getPrizeName());
                    recordDTO.setPrizeTier(ActivityTiersEnum.forName(winningRecordDO.getPrizeTier()));
                    recordDTO.setWinningTime(winningRecordDO.getWinningTime());
                    return recordDTO;
                }).collect(Collectors.toList());
    }


    /**
     * 从缓存中删除中奖记录
     *
     * @param key
     */
    private void deleteWinningRecords(String key) {
        try {
            if (redisUtil.hasKey(WINNING_RECORDS_PREFIX + key)) {
                redisUtil.del(WINNING_RECORDS_PREFIX + key);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 缓存中奖记录
     *
     * @param key
     * @param winningRecordDOList
     * @param winningRecordsTimeOut
     */
    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winningRecordDOList,
                                     Long winningRecordsTimeOut) {
        if (!StringUtils.hasLength(key) || winningRecordDOList == null) {
            return;
        }
        try {
            redisUtil.set(WINNING_RECORDS_PREFIX + key,
                    JacksonUtil.writeValueAsString(winningRecordDOList),
                    winningRecordsTimeOut);
        } catch (Exception e) {
            log.error("缓存中奖记录异常{}", WINNING_RECORDS_PREFIX + key);
            e.printStackTrace();
        }
    }

    private List<WinningRecordDO> getWinningRecords(String key) {

        try {
            if (key == null) {
                return Arrays.asList();
            }
            String s = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasLength(s)) {
                return Arrays.asList();
            }
            return JacksonUtil.readListValue(s, WinningRecordDO.class);
        } catch (Exception e) {
            log.error("从缓存中查询中奖记录异常{}", WINNING_RECORDS_PREFIX + key);
            e.printStackTrace();
            return Arrays.asList();
        }
    }
}
